//////////////////////////////////////////////////////////////////////
// grflibParticleSys.fx
//
// SHEN Fangyang
// me@shenfy.com
//
// Copyright (C) SHEN Fangyang, 2011, All rights reserved.
//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
//  Brief Description
//
//	VS/GS/PS for billboard particle rendering
//
//////////////////////////////////////////////////////////////////////

// structs
struct VSParticleIn
{
    float3 Pos			  : POSITION;
    float3 Vel			  : VELOCITY;
	float Angle			  : ANGLE;
    float Life			  : LIFE;
    float Size			  : SIZE;
};

struct GSParticleIn
{
    float3 Pos            : POSITION;
	float Angle			  : ANGLE;
    float Life            : LIFE;
    float Size            : SIZE;
};

struct PSParticleIn
{
    float4 Pos			  : SV_POSITION;
    float2 Tex			  : TEXCOORD0;
    float2 Depth		  : TEXCOORD1;
};


cbuffer view_param
{
	//transformation
	matrix World;
	matrix View;
	matrix Projection;
	matrix InvView;
};

cbuffer cbImmutable
{
    float3 billboard_corners[4] =
    {
        float3( -1, 1, 0 ),
        float3( 1, 1, 0 ),
        float3( -1, -1, 0 ),
        float3( 1, -1, 0 ),
    };

    float2 billboard_texcoords[4] = 
    { 
        float2(0,0), 
        float2(1,0),
        float2(0,1),
        float2(1,1),
    };
};

Texture2D DiffuseTexture;

SamplerState samLinearWrap
{
    Filter = MIN_MAG_MIP_LINEAR;
    AddressU = Wrap;
    AddressV = Wrap;
};

BlendState AlphaBlending
{
    AlphaToCoverageEnable = FALSE;
    BlendEnable[0] = TRUE;
    SrcBlend = SRC_ALPHA;
    DestBlend = INV_SRC_ALPHA;
    BlendOp = ADD;
    SrcBlendAlpha = ZERO;
    DestBlendAlpha = ZERO;
    BlendOpAlpha = ADD;
    RenderTargetWriteMask[0] = 0x0F;
};

DepthStencilState DisableDepthWrite
{
    DepthEnable = TRUE;
    DepthWriteMask = ZERO;
};


// Vertex shader for drawing particles
//
GSParticleIn VSParticlemain(VSParticleIn input)
{
    GSParticleIn output;
    
    output.Pos = input.Pos;
    output.Life = input.Life;
    output.Size = input.Size;
	output.Angle = input.Angle;
    
    return output;
}

//
// Geometry shader for creating point sprites
//
[maxvertexcount(4)]
void GSParticlemain(point GSParticleIn input[1], inout TriangleStream<PSParticleIn> SpriteStream)
{
    PSParticleIn output;
    
	matrix rotmat = {cos(input[0].Angle), -sin(input[0].Angle), 0, 0,
					 sin(input[0].Angle), cos(input[0].Angle), 0, 0,
					 0, 0, 1, 0,
					 0, 0, 0, 1};
    // Emit two new triangles
    //
    [unroll] for(int i=0; i<4; i++)
    {
        float3 position = billboard_corners[i] * input[0].Size;

		//rotation
		position = mul( position, (float3x3)rotmat );
        position = mul( position, (float3x3)InvView ) + input[0].Pos;
        output.Pos = mul( float4(position, 1.0), World );
        output.Pos = mul( output.Pos, View );
		output.Pos = mul( output.Pos, Projection );

        // pass along the texcoords
        output.Tex = billboard_texcoords[i];
        // output depth of this particle
        output.Depth = output.Pos.zw;

        SpriteStream.Append(output);
    }

    SpriteStream.RestartStrip();
}


//
// PS for the particles
//
float4 PSBillboardParticlemain(PSParticleIn input) : SV_TARGET
{     
    float4 particleSample = DiffuseTexture.Sample(samLinearWrap, input.Tex);
    
    //float particleDepth = input.Depth.x;
    //particleDepth /= input.Depth.y;
    
    return float4(particleSample.xyz, particleSample.x);
}

struct PSParticleOut
{
    float4 Color : SV_Target;
    float Depth : SV_Depth;
};


//
// RenderBillboardParticles_Hard
//
technique10 RenderBillboardParticles
{
    pass p0
    {
        SetVertexShader( CompileShader( vs_4_0, VSParticlemain() ) );
        SetGeometryShader( CompileShader( gs_4_0, GSParticlemain() ) );
        SetPixelShader( CompileShader( ps_4_0, PSBillboardParticlemain() ) );
        
        SetBlendState( AlphaBlending, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF );
        SetDepthStencilState( DisableDepthWrite, 0 );
    }  
}
